/**
 * Copyright (c) 2012-2014, Yang Yang <cntototo@gmail.com>
 *
 * This software may be distributed under the terms of the New BSD License.
 * See README for more details.
 */

#ifndef UTIL_H
#define UTIL_H

#include "def.h"
#include "include.h"
#include "driver.h"

/** 16 bits byte swap */
#define swap_byte_16(x) \
	((u16)(	(((u16)(x) & 0x00ffU) << 8) | \
		(((u16)(x) & 0xff00U) >> 8)))

/** 32 bits byte swap */
#define swap_byte_32(x) \
	((u32)(	(((u32)(x) & 0x000000ffUL) << 24) | \
		(((u32)(x) & 0x0000ff00UL) <<  8) | \
		(((u32)(x) & 0x00ff0000UL) >>  8) | \
		(((u32)(x) & 0xff000000UL) >> 24)))

/** Endian to host convert */
#if (BYTE_ORDER == LITTLE_ENDIAN)

#define le_to_host16(x) (x)
#define host_to_le16(x) (x)
#define be_to_host16(x) swap_byte_16(x)
#define host_to_be16(x) swap_byte_16(x)

#define le_to_host32(x) (x)
#define host_to_le32(x) (x)
#define be_to_host32(x) swap_byte_32(x)
#define host_to_be32(x) swap_byte_32(x)

#elif (BYTE_ORDER == BIG_ENDIAN)

#define le_to_host16(x) swap_byte_16(x)
#define host_to_le16(x) swap_byte_16(x)
#define be_to_host16(x) (x)
#define host_to_be16(x) (x)

#define le_to_host32(x) swap_byte_32(x)
#define host_to_le32(x) swap_byte_32(x)
#define be_to_host32(x) (x)
#define host_to_be32(x) (x)

#else
#error Unknown byte order
#endif

/** Safe free */
#define FREE(x) do {		\
	if (x) {		\
		free(x);	\
		x = NULL;	\
	}			\
} while (0)

/** Safe close */
#define CLOSE(x) do {		\
	if (x >= 0)		\
		close(x);	\
} while (0)

/** Safe fclose */
#define FCLOSE(x) do {		\
	if (x)			\
		fclose(x);	\
} while (0)

/** Array size */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

#ifndef MIN
#define MIN(X, Y) ({		\
	typeof(X) _x = (X);	\
	typeof(Y) _y = (Y);	\
	(void) (&_x == &_y);	\
	(_x < _y) ? _x : _y; })
#endif

#ifndef MAX
#define MAX(X, Y) ({		\
	typeof(X) _x = (X);	\
	typeof(Y) _y = (Y);	\
	(void) (&_x == &_y);	\
	(_x > _y) ? _x : _y; })
#endif

extern u8 zero_addr[ETH_ALEN];

static inline u32 get_be32(const u8 *a)
{
	return a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3];
}

static inline int is_zero_mac_addr(const u8 *a)
{
	return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
}

static inline int is_broadcast_mac_addr(const u8 *a)
{
	return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
}

static inline int is_same_mac(const u8 *a, const u8 *b)
{
	return memcmp(a, b, ETH_ALEN) == 0;
}

static inline void *zalloc(size_t size)
{
	void *ptr = NULL;
	if (size == 0)
		return ptr;
	ptr = malloc(size);
	if (ptr)
		memset(ptr, 0, size);
	return ptr;
}

/**
 * build_fail_resp -- Build failure response message
 */
char *build_fail_resp(void);

/**
 * build_ok_resp -- Build OK response message
 */
char *build_ok_resp(void);

/**
 * cnt_chr_in_str -- Count the number of chr in str
 */
u32 cnt_chr_in_str(const char *str, const char chr);

/**
 * err_to_str -- Get description string of error
 */
const char *err_to_str(enum wfs_internal_err err);

/**
 * free_assoc_params -- Free assoc params structure
 */
void free_assoc_params(struct wfs_driver_assoc_params *params);

/**
 * free_hw_info -- Free hw info structure
 */
void free_hw_info(struct wfs_driver_hw_info *hw_info, u32 num);

/**
 * free_scan_params -- Free scan params structure
 */
void free_scan_params(struct wfs_driver_scan_params *params);

/**
 * free_scan_result -- Free scan result structure
 */
void free_scan_result(struct wfs_driver_scan_result *result);

/**
 * free_scan_results -- Free scan results structure
 */
void free_scan_results(struct wfs_driver_scan_results *results);

/**
 * freq_to_chan -- Frequency to channel
 */
enum wfs_driver_hw_mode
freq_to_chan(u32 freq, u8 *chan);

/**
 * get_akm_suite -- Get AKM suite
 */
int get_akm_suite(enum wfs_key_mgmt mgmt);

/**
 * get_bss_by_bssid -- Get the scan result that matches BSSID
 */
const struct wfs_driver_scan_result *
get_bss_by_bssid(const struct wfs_driver_scan_results *results,
		const u8 *bssid);

/**
 * get_bss_by_ssid -- Get the scan result that matches SSID
 */
const struct wfs_driver_scan_result *
get_bss_by_ssid(const struct wfs_driver_scan_results *results,
		const u8 *ssid, u32 ssid_len);

/**
 * get_ch_width_str -- Get channel width string
 */
const char *get_ch_width_str(enum wfs_ch_width width);

/**
 * get_cipher_suite -- Get cipher suite
 */
int get_cipher_suite(enum wfs_cipher cipher);

/**
 * get_cipher_suites -- Get cipher suites
 */
int get_cipher_suites(enum wfs_cipher ciphers, u32 *suites, int max_suites);

/**
 * get_ie_elem -- Get the position of element (by EID) in IE
 */
const u8 *get_ie_elem(const u8 *ie, u32 ie_len, u8 eid);

/**
 * get_ie_max_rate -- Get the maximum supported rate in IE
 */
u32 get_ie_max_rate(const u8 *ie, u32 ie_len);

/**
 * get_ie_ssid -- Get SSID in IE
 */
const u8 *get_ie_ssid(const u8 *ie, u32 ie_len, u32 *ssid_len);

/**
 * get_ie_vendor -- Get vendor specific in IE
 */
const u8 *get_ie_vendor(const u8 *ie, u32 ie_len, u32 vendor_type);

/**
 * get_ssid_str -- Get SSID printable string
 */
const char *get_ssid_str(const u8 *ssid, u32 len);

/**
 * get_tdls_oper_str -- Get TDLS operation string
 */
const char *get_tdls_oper_str(enum wfs_tdls_oper oper);

/**
 * get_u32_array_str -- Get string of u32 array
 */
const char *get_u32_array_str(const u32 *arr, u32 len, const char *delim);

/**
 * hexstr2bin -- Convert ASCII hex string into binary array
 */
enum wfs_internal_err
hexstr2bin(const char *hex, u8 *buf, u32 len);

/**
 * mem_dup -- duplicate memory
 */
void *mem_dup(const void *src, u32 len);

/**
 * str_strip -- Remove leading and trailing whitespaces
 */
char *str_strip(char *s);

#endif /* UTIL_H */
